package com.yuxl.common.auth.service;

import cn.hutool.core.util.StrUtil;
import com.yuxl.common.auth.domain.UserInfo;
import com.yuxl.common.auth.entity.AuthMenu;
import com.yuxl.common.auth.entity.AuthPermission;
import com.yuxl.common.auth.entity.AuthRole;
import com.yuxl.common.auth.entity.AuthUser;
import com.yuxl.common.auth.repository.AuthUserRepository;
import jakarta.annotation.Resource;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class AuthUserService {
    @Resource
    private AuthUserRepository authUserRepository;
    @Resource
    private AuthUserRoleService authUserRoleService;
    @Resource
    private AuthRoleMenuService authRoleMenuService;
    @Resource
    private AuthRolePermissionService  authRolePermissionService;

    private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();

    public Flux<AuthUser> findAll() {
        return authUserRepository.findAll();
    }

    public Mono<AuthUser> findById(Long id) {
        return authUserRepository.findById(id)
                .defaultIfEmpty(new AuthUser());
    }

    public Mono<Void> deleteById(Long id) {
        return authUserRepository.deleteById(id);
    }

    public Mono<Void> deleteByIds(List<Long> ids) {
        return authUserRepository.deleteAllById(ids);
    }

    public Mono<AuthUser> findByUsername(String username) {
        return authUserRepository.findByUsername(username);
    }

    public Mono<AuthUser> create(AuthUser user) {
        if(StrUtil.isNotEmpty(user.getPassword())){
            user.setPassword(passwordEncoder.encode(user.getPassword()));
        }else{
            user.setPassword(passwordEncoder.encode("123456"));
        }

        user.setCreateTime(LocalDateTime.now()).setUpdateTime(LocalDateTime.now());
        return authUserRepository.save(user);
    }

    public Mono<Integer> count(AuthUser user) {
        return authUserRepository.count(user);
    }


    public Flux<AuthUser> page(AuthUser user, int page, int size) {
        long offset = (long) (page - 1) * size;
        return authUserRepository.page(user, size, offset);
    }


    public Flux<AuthUser> list(AuthUser user) {
        return authUserRepository.list(user);
    }

    public Mono<AuthUser> updateById(AuthUser user) {
        user.setUpdateTime(LocalDateTime.now());
        return authUserRepository.updateById(user);
    }

    public Mono<AuthUser> updatePassword(AuthUser user) {
        return authUserRepository.updatePassword(user)
                .flatMap(rows -> rows > 0 ? authUserRepository.findById(user.getId()) : Mono.empty());
    }

    public Flux<AuthRole> getRolesById(long id) {
        return authUserRoleService.findByUserId(id);
    }

    public Mono<UserInfo> getUserInfo(AuthUser entity) {
        return authUserRoleService.findByUserId(entity.getId())
                .collectList()
                .flatMap(roles -> {
                    List<String> roleMarks = roles.stream().map(AuthRole::getMark).toList();
                    return Mono.zip(
                            authRoleMenuService.findByRoleMarks(roleMarks)
                                    .collectList(),
                            authRolePermissionService.findByRoleMarks(roleMarks)
                                    .collectList()
                    ).map(tuple -> {
                        List<String> menus = tuple.getT1().stream().map(AuthMenu::getName).toList();
                        List<String> permissions = tuple.getT2().stream().map(AuthPermission::getMark).toList();
                        return new UserInfo()
                                .setRoles(roleMarks)
                                .setMenus(menus)
                                .setPermissions(permissions)
                                .setId(entity.getId())
                                .setAvatar(entity.getAvatar())
                                .setUsername(entity.getUsername())
                                .setNickname(entity.getNickname());
                    });
                });
    }

    public Flux<AuthUser> initializeUser() {
        List<AuthUser> initUsers;
        initUsers = List.of(
                new AuthUser()
                        .setNickname("admin")
                        .setUsername("admin")
                        .setPassword(passwordEncoder.encode("admin123"))
                        .setCreateTime(LocalDateTime.now())
                        .setUpdateTime(LocalDateTime.now())
                ,
                new AuthUser()
                        .setNickname("user")
                        .setUsername("user")
                        .setPassword(passwordEncoder.encode("user123456"))
                        .setCreateTime(LocalDateTime.now())
                        .setUpdateTime(LocalDateTime.now())
        );
        return authUserRepository.saveAll(initUsers);
    }


}